/*
 * Copyright 2011 Kim Lindhardt Madsen
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */

package dk.lindhardt.gwt.geie.client;

import com.google.gwt.event.dom.client.*;
import com.google.gwt.event.logical.shared.AttachEvent;
import com.google.gwt.regexp.shared.RegExp;
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.Element;
import com.google.gwt.user.client.ui.AbsolutePanel;
import com.google.gwt.user.client.ui.HTMLTable;
import dk.lindhardt.gwt.geie.shared.Image;
import dk.lindhardt.gwt.geie.shared.TableLayout;

import java.util.ArrayList;
import java.util.List;

/**
 * User: AnAmuser
 * Date: 14-07-11
 * <p/>
 * An extension of gwt's normal flex table. Wraps a flex table in an absolute panel for showing images.
 */
public class GeiePanel extends AbsolutePanel {

   private TableLayout layout;
   private HTMLTable gwtTable;

   private boolean showImages = true;

   /**
    * Builds a flex table from the table layout and shows images on top of the table.
    * @param layout the layout to build table from
    */
   public GeiePanel(TableLayout layout) {
      super();
      this.layout = layout;
      TableLayout2FlexTable builder = new TableLayout2FlexTable(layout);
      gwtTable = builder.build();
      add(gwtTable, -1, -1);

      addAttachHandler(
            new AttachEvent.Handler() {
               public void onAttachOrDetach(AttachEvent event) {
                  if (clientBrowserSupportsDataURIs() && showImages && GeiePanel.this.gwtTable != null && event.isAttached()) {
                     attachImages();
                  }
               }
            });
   }

   private boolean clientBrowserSupportsDataURIs() {
      float ieVersion = Util.getIEVersion();
      return ieVersion > 7.2 || ieVersion == 0;
   }

   /**
    * Attaches images to this panel
    */
   private void attachImages() {
      Integer[] rowHeights = getRowHeights();
      Integer[] columnWidths = getColumnWidths();
      for (Image image : layout.getImages()) {
         DraggableImage draggableImage = new DraggableImage(image.getBase64Data());
         int width = (int) Util.sum(columnWidths, image.getX(), image.getWidth(), 64.0);
         int height = (int) Util.sum(rowHeights, image.getY(), image.getHeight(), 17.0);
         draggableImage.setPixelSize(width, height);
         add(
               draggableImage,
               (int) Util.sum(columnWidths, 0, image.getX(), 64.0),
               (int) Util.sum(rowHeights, 0, image.getY(), 17.0));
      }
   }

   /**
    * @return An array of row heights
    */
   private Integer[] getRowHeights() {
      Integer[] rowHeights = new Integer[gwtTable.getRowCount()];
      for (int i = 0; i < gwtTable.getRowCount(); i++) {
         Element row = gwtTable.getRowFormatter().getElement(i);
         int height = row.getClientHeight();
         rowHeights[i] = height;
      }
      return rowHeights;
   }

   /**
    * @return An array of column widths
    */
   private Integer[] getColumnWidths() {
      List<Integer> columnWidths = new ArrayList<Integer>();
      for (int i = 0; i < gwtTable.getRowCount(); i++) {
         int cellCount = gwtTable.getCellCount(i);
         for (int j = 0; j < cellCount; j++) {
            Element column = gwtTable.getColumnFormatter().getElement(j);
            int width = column.getClientWidth();
            if (columnWidths.size() <= j) {
               columnWidths.add(width);
            } else if (columnWidths.get(j) > width) {
               columnWidths.set(j, width);
            }
         }
      }
      return columnWidths.toArray(new Integer[columnWidths.size()]);
   }

   /**
    * Is images shown
    * @return whether images is to be shown
    */
   public boolean isShowImages() {
      return showImages;
   }

   /**
    * Sets whether images is shown
    * @param showImages show images
    */
   public void setShowImages(boolean showImages) {
      this.showImages = showImages;
   }

   /**
    * Image that can be dragged around in the panel
    */
   class DraggableImage extends com.google.gwt.user.client.ui.Image {

      private boolean dragging = false;
      private int x;
      private int y;
      private int left;
      private int top;

      /**
       * An image that can be dragged
       * @param base64Data image data
       */
      public DraggableImage(String base64Data) {
         super("data:image/png;base64," + base64Data);

         addMouseDownHandler(
               new MouseDownHandler() {
                  public void onMouseDown(MouseDownEvent event) {
                     event.preventDefault();
                     dragging = true;
                     left = GeiePanel.this.getWidgetLeft(DraggableImage.this);
                     top = GeiePanel.this.getWidgetTop(DraggableImage.this);
                     x = event.getClientX();
                     y = event.getClientY();
                  }
               });
         addMouseMoveHandler(
               new MouseMoveHandler() {
                  public void onMouseMove(MouseMoveEvent event) {
                     event.preventDefault();
                     if (dragging) {
                        int x_ = event.getClientX();
                        int dx = x_ - x;
                        int y_ = event.getClientY();
                        int dy = y_ - y;
                        left = left + dx;
                        top = top + dy;
                        x = x_;
                        y = y_;
                        GeiePanel.this.setWidgetPosition(DraggableImage.this, left, top);
                     }
                  }
               });
         addMouseUpHandler(
               new MouseUpHandler() {
                  public void onMouseUp(MouseUpEvent event) {
                     dragging = false;
                  }
               });
         addMouseOutHandler(
               new MouseOutHandler() {
                  public void onMouseOut(MouseOutEvent event) {
                     dragging = false;
                  }
               });
         // Setting z index on picture on higher than the table
         float ieVersion = Util.getIEVersion();
         String zIndex = Util.getUsedStyle(ieVersion, gwtTable.getElement(), "z-index");
         if (RegExp.compile("\\d+").test(zIndex)) {
            DOM.setStyleAttribute(getElement(), "zIndex", (Integer.parseInt(zIndex) + 1) + "");
         }
      }
   }
}
